package com.neusoft.hifly.dvpt.util;

import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.lang3.StringUtils;

import com.neusoft.hifly.commons.database.DBManager;
import com.neusoft.hifly.commons.database.DBUtil;
import com.neusoft.hifly.commons.database.SQLUtil;
import com.neusoft.hifly.commons.database.TableUtil;
import com.neusoft.hifly.commons.file.FileUtil;
import com.neusoft.hifly.commons.id.IdWorker;
import com.neusoft.hifly.commons.lang.StringConstants;
import com.neusoft.hifly.commons.lang.StringUtil;
import com.neusoft.hifly.dvpt.domain.Contact;
import com.neusoft.hifly.dvpt.domain.DataBaseInfo;
import com.neusoft.hifly.dvpt.domain.PathInfo;

public class EngineerUtil {
	private final static String FILE_UNCODE = "UTF-8";

	private DataBaseInfo dbInfo;

	// 是否生成逻辑删除
	private boolean logicDel = false;

	// 是否使用mybatis
	private boolean mybatis = true;

	/**
	 * 从数据库逆向生成代码
	 */
	public void engineer(final PathInfo pathInfo, final DataBaseInfo dbInfo, final Contact contact,
			final String beforeName, final boolean logicDel, final boolean mybatis, final boolean isExcel) {
		this.dbInfo = dbInfo;
		this.logicDel = logicDel;
		this.mybatis = mybatis;

		final DBManager db = new DBManager(DBUtil.getInstanse().connectionDriver(dbInfo.getDbType()),
				DBUtil.getInstanse().connectionUrl(dbInfo.getDbType(), dbInfo.getIp(), dbInfo.getPort(),
						dbInfo.getSchema(), dbInfo.getCharacterEncoding()),
				dbInfo.getUser(), dbInfo.getPassword()); //创建数据库对象
		try {

			if (db.OpenConnection()) {
				try {
					// 查询满足规则的所有表名
					final ResultSet rs1 = db.ExecuteQuery(TableUtil.getInstanse().generateAllTableNamesDDL(
							dbInfo.getDbType(), beforeName, dbInfo.getExecptName(), dbInfo.getSchema()));
					String tableName;
					String tableComment;
					String objPath;
					while (rs1.next()) {

						tableName = rs1.getString("tableName");
						tableComment = getTableComment(rs1.getString("tableComment"));
						final String className = getClassName(tableName);
						if (StringUtils.isEmpty(className)) {
							continue;
						}
						System.out.println("===== T ======" + tableName);

						final Map<String, String> map = new HashMap<String, String>(1);

						map.put("\\{project.author\\}", contact.getAuthor()); // 开发人员：{project.author}
						map.put("\\{project.version\\}", contact.getVersion()); // 版本：{project.version}
						map.put("\\{project.name\\}", contact.getName()); // 工程名称：{project.name}
						map.put("\\{project.app\\}", contact.getApp()); // 工程名称：{project.app}
						map.put("\\{project.copyright\\}", contact.getCopyright()); // {project.copyright}
						map.put("\\{project.company\\}", contact.getCompany()); // {project.company}

						map.put("\\{package\\}", contact.getPackageName()); // 包名：{package}
						map.put("\\{obj\\}", className); // 对象名：{obj}
						map.put("\\{obj.lower\\}", StringUtils.lowerCase(className)); // 对象小写名：{obj.lower}
						map.put("\\{obj.hump\\}", StringUtils.uncapitalize(className)); // 对象驼峰命名：{obj.hump}
						map.put("\\{obj.name\\}", tableComment); // 对象备注：{obj.name}
						map.put("\\{table.name\\}", StringUtils.upperCase(tableName)); // 表名
						map.put("\\{multi.table.name\\}",
								StringUtils.upperCase(tableName).substring(tableName.indexOf("_") + 1)); // 表名

						// 将对象名称首字母大写，其他字幕小写，若中间有下划线，则两单词首字母大写

						// 根据表创建一个包，名称为小写字母
						objPath = pathInfo.getReversePath() + File.separator + beforeName + File.separator
								+ StringUtils.lowerCase(className);

						if (logicDel) {
							FileUtil.copyFolder(pathInfo.getDelPath(), objPath);
						} else {
							FileUtil.copyFolder(pathInfo.getTempPath(), objPath);
						}

						// 根据数据表查询所有列
						fields(tableName, className, map, isExcel);

						// 替换{obj}.java文件名称，以及文件中的标签
						replaceFileName(objPath, className, map);
					}
					rs1.close();
				} catch (final SQLException e) {
					System.out.print(e.getMessage());
				}
			}
		} finally {
			db.CloseConnection();
		}
	}

	private void fields(final String tableName, final String className, final Map<String, String> labMap,
			final boolean isExcel) {
		final DBManager db = new DBManager(DBUtil.getInstanse().connectionDriver(dbInfo.getDbType()),
				DBUtil.getInstanse().connectionUrl(dbInfo.getDbType(), dbInfo.getIp(), dbInfo.getPort(),
						dbInfo.getSchema(), dbInfo.getCharacterEncoding()),
				dbInfo.getUser(), dbInfo.getPassword()); //创建数据库对象
		try {

			// Oracle别名不能用model，统一改成info
			final String model = "info";

			final IdWorker idWorker = new IdWorker(new Random().nextInt(30) + 1, 1);

			final StringBuffer param = new StringBuffer("");
			final StringBuffer delParam = new StringBuffer("");
			final StringBuffer getter = new StringBuffer("");
			final StringBuffer mapper = new StringBuffer("");
			final StringBuffer mapperBatch = new StringBuffer("");
			final StringBuffer mapperBatchValues = new StringBuffer("");
			final StringBuffer vueTemp = new StringBuffer("");
			final StringBuffer serviceParam = new StringBuffer("");
			final StringBuffer insertParam = new StringBuffer("");
			final StringBuffer insertNum = new StringBuffer("");
			final StringBuffer insertField = new StringBuffer("");

			final StringBuffer updateParam = new StringBuffer("");
			final StringBuffer findField = new StringBuffer("");
			final StringBuffer findSetform = new StringBuffer("");
			final StringBuffer searchReplace = new StringBuffer("");
			String orderField = null;

			if (db.OpenConnection()) {
				try {
					// 查询满足规则的所有表名。英文列名（字段名）columnName、列注释columnComment、列类型dataType、 列大小dataLength
					final ResultSet rs1 = db.ExecuteQuery(TableUtil.getInstanse()
							.generateAllFieldsDDL(dbInfo.getDbType(), tableName, dbInfo.getSchema()));

					String idFiled = "";
					String logicIdFiled = "";
					String columnName = "";
					String columnComment = "";
					String dataType = "";
					String fieldName = "";
					String fieldType = "";
					String findFieldName = "";
					final StringBuffer paramTemp = new StringBuffer("");
					boolean isId = false;
					boolean isLogicId = false;
					while (rs1.next()) {
						isId = false;
						isLogicId = false;
						columnName = rs1.getString("columnName");
						columnComment = rs1.getString("columnComment");
						dataType = rs1.getString("dataType");
						rs1.getLong("dataLength");

						if (StringUtils.upperCase(columnName).indexOf("ORDER") >= 0) {
							orderField = StringUtils.upperCase(columnName);
						}

						// 配件对象属性
						fieldName = getFieldName(tableName, className, columnName);
						if (StringUtils.isEmpty(fieldName)) {
							continue;
						}

						fieldType = SQLUtil.dbFieldToJava(dataType);

						// 主键单独处理
						if ((delBeforeName(tableName) + "_id").equalsIgnoreCase(columnName)
								|| "id".equalsIgnoreCase(columnName)) {
							idFiled = columnName;
							isId = true;
							fieldName = "id";
						}
						if ((delBeforeName(tableName) + "_logic_id").equalsIgnoreCase(columnName)
								|| "logic_id".equalsIgnoreCase(columnName)) {
							logicIdFiled = columnName;
							isLogicId = true;
							fieldName = "logicId";
						}

						// dao 中的insert方法，不要id和logic_id
						if (!isId && !isLogicId) {
							insertParam.append(" " + StringUtils.upperCase(columnName) + ",");
							insertNum.append(" ?,");
							insertField.append(" obj." + getFieldGetterName(fieldName, fieldType) + "(),");
							updateParam.append(" " + StringUtils.upperCase(columnName) + " = ?,");

							// 查询数据库时，name,code,order等名称不能作为别名，需要替换
							if ("name".equalsIgnoreCase(fieldName) || "code".equalsIgnoreCase(fieldName)
									|| "mode".equalsIgnoreCase(fieldName) || "type".equalsIgnoreCase(fieldName)
									|| "order".equalsIgnoreCase(fieldName) || "interval".equalsIgnoreCase(fieldName)
									|| "where".equalsIgnoreCase(fieldName) || "from".equalsIgnoreCase(fieldName)
									|| "key".equalsIgnoreCase(fieldName) || "status".equalsIgnoreCase(fieldName)
									|| "abstract".equalsIgnoreCase(fieldName) || "private".equalsIgnoreCase(fieldName)
									|| "public".equalsIgnoreCase(fieldName) || "time".equalsIgnoreCase(fieldName)
									|| "mysql".equalsIgnoreCase(fieldName) || "range".equalsIgnoreCase(fieldName)
									|| "oracle".equalsIgnoreCase(fieldName)) {
								findFieldName = model + StringUtils.capitalize(fieldName);
							} else {
								findFieldName = fieldName;
							}

							findField.append(
									" " + model + "." + StringUtils.upperCase(columnName) + " " + findFieldName + ",");
							findSetform.append("		vo." + getFieldSetterName(fieldName, fieldType) + "(rs.get"
									+ getResultSetType(dataType) + "(\"" + findFieldName + "\"));"
									+ StringConstants.TAG_R_N);

							searchReplace.append("			if (property.containsKey(\"" + fieldName + "\")) {"
									+ StringConstants.TAG_R_N);
							searchReplace.append("				property.put(\"" + StringUtils.upperCase(columnName)
									+ "\", property.get(\"" + fieldName + "\"));" + StringConstants.TAG_R_N);
							searchReplace.append(
									"				property.remove(\"" + fieldName + "\");" + StringConstants.TAG_R_N);
							searchReplace.append("			}" + StringConstants.TAG_R_N);

							if (!"CREATE_PERSON".equalsIgnoreCase(columnName)
									&& !"CREATE_PERSON_ID".equalsIgnoreCase(columnName)
									&& !"CREATE_TIME".equalsIgnoreCase(columnName)
									&& !"UPDATE_PERSON".equalsIgnoreCase(columnName)
									&& !"UPDATE_PERSON_ID".equalsIgnoreCase(columnName)
									&& !"UPDATE_TIME".equalsIgnoreCase(columnName)) {
								if ("String".equalsIgnoreCase(fieldType)) {
									// 字符串的需要去掉左右空格
									serviceParam.append("		toObj." + getFieldSetterName(fieldName, fieldType)
											+ "(StringUtils.trimToEmpty(fromObj."
											+ getFieldGetterName(fieldName, fieldType) + "()));"
											+ StringConstants.TAG_R_N);
								} else {
									serviceParam.append("		toObj." + getFieldSetterName(fieldName, fieldType)
											+ "(fromObj." + getFieldGetterName(fieldName, fieldType) + "());"
											+ StringConstants.TAG_R_N);
								}
							}
						}

						// 定义属性(非ID的)
						paramTemp.append("	// " + columnComment + StringConstants.TAG_R_N);

						if (mybatis) {
							if ("ID".equalsIgnoreCase(columnName)) {
								paramTemp.append("	@TableId(value = \"" + StringUtils.upperCase(columnName) + "\") "
										+ StringConstants.TAG_R_N);
								paramTemp.append("	@ApiModelProperty(value = \"" + columnComment + "\") "
										+ StringConstants.TAG_R_N);
							} else {
								if (isExcel) {
									paramTemp.append(
											"	@Excel(name = \"" + columnComment + "\") " + StringConstants.TAG_R_N);
								}
								paramTemp.append("	@TableField(value = \"" + StringUtils.upperCase(columnName) + "\") "
										+ StringConstants.TAG_R_N);
								paramTemp.append("	@ApiModelProperty(value = \"" + columnComment + "\") "
										+ StringConstants.TAG_R_N);
							}
						}

						if ("LocalDateTime".equals(fieldType)) {
							paramTemp.append(
									"	@JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\") " + StringConstants.TAG_R_N);
							paramTemp.append("	@JsonDeserialize(using = LocalDateTimeDeserializer.class) "
									+ StringConstants.TAG_R_N);
							paramTemp.append("	@JsonSerialize(using = LocalDateTimeSerializer.class) "
									+ StringConstants.TAG_R_N);
						}

						// 逻辑删除的字段属性域必须是protected
						paramTemp.append((logicDel ? "	protected " : "	private ") + fieldType + " " + fieldName + ";"
								+ StringConstants.TAG_R_N);

						// getter/setter方法

						getter.append("	public " + fieldType + " " + getFieldGetterName(fieldName, fieldType) + "() {"
								+ StringConstants.TAG_R_N);
						getter.append("		return " + fieldName + ";" + StringConstants.TAG_R_N);
						getter.append("	}" + StringConstants.TAG_R_N);
						getter.append("	public void " + getFieldSetterName(fieldName, fieldType) + "(final " + fieldType
								+ " " + fieldName + ") {" + StringConstants.TAG_R_N);
						getter.append("		this." + fieldName + " = " + fieldName + ";" + StringConstants.TAG_R_N);
						getter.append("	}" + StringConstants.TAG_R_N);

						if (!"ID".equalsIgnoreCase(columnName)) {
							mapper.append("		<result column=\"" + StringUtils.upperCase(columnName)
									+ "\" property=\"" + fieldName + "\" />" + StringConstants.TAG_R_N);
						}

						mapperBatch.append(columnName + ", ");
						mapperBatchValues.append("#{item." + fieldName + "}, ");
						vueTemp.append("" + fieldName + ": '',\r\n");
					}

					if (!mybatis) {
						param.append("	public static final String TABLE_NAME = \""
								+ org.apache.commons.lang3.StringUtils.upperCase(tableName)
										.substring(tableName.indexOf("_") + 1)
								+ "\";	" + StringConstants.TAG_R_N);
						param.append("	public static final String TABLE_ID = \""
								+ StringUtils.upperCase(StringUtils.trimToEmpty(idFiled)) + "\";	"
								+ StringConstants.TAG_R_N);
						param.append("	public static final String TABLE_LOGIC_ID = \""
								+ StringUtils.upperCase(StringUtils.trimToEmpty(logicIdFiled)) + "\";	"
								+ StringConstants.TAG_R_N);
					}

					param.append("	private static final long serialVersionUID = -" + idWorker.nextId() + "L;	"
							+ StringConstants.TAG_R_N);

					param.append(paramTemp);

					delParam.append("	public static final String TABLE_NAME = \"" + StringUtils.upperCase(tableName)
							+ "_DEL\";	" + StringConstants.TAG_R_N);
					delParam.append("	private final long serialVersionUID = -" + idWorker.nextId() + "L;	"
							+ StringConstants.TAG_R_N);

					rs1.close();
				} catch (final SQLException e) {
					e.printStackTrace();
					System.out.print(e.getMessage());
				}
			}
			labMap.put("\\{param\\}", param.toString()); // 对象属性：{param}

			labMap.put("\\{serialVersionUID\\}", idWorker.nextId() + "");

			labMap.put("\\{del_param\\}", delParam.toString()); // 逻辑删除对象属性：{del_param}

			if (insertParam.length() >= 1) {
				insertParam.delete(insertParam.length() - 1, insertParam.length());
			}
			if (insertNum.length() >= 1) {
				insertNum.delete(insertNum.length() - 1, insertNum.length());
			}
			if (insertField.length() >= 1) {
				insertField.delete(insertField.length() - 1, insertField.length());
			}
			if (updateParam.length() >= 1) {
				updateParam.delete(updateParam.length() - 1, updateParam.length());
			}
			if (findField.length() >= 1) {
				findField.delete(findField.length() - 1, findField.length());
			}
			labMap.put("\\{dao.insert.param\\}", insertParam.toString()); // dao层的insert方法参数：{dao.insert.param}
			labMap.put("\\{dao.insert.num\\}", insertNum.toString()); // dao层的insert方法参数问号：{dao.insert.num}
			labMap.put("\\{dao.insert.field\\}", insertField.toString()); // dao层的insert方法对象取值{dao.insert.field}
			labMap.put("\\{dao.update.param\\}", updateParam.toString()); // dao层的update方法参数{dao.update.param}
			labMap.put("\\{dao.find.field\\}", findField.toString()); // dao层的查询方法对象取值{dao.find.field}
			labMap.put("\\{dao.find.setform\\}", findSetform.toString()); // dao层的查询方法对象赋值{dao.find.setform}
			labMap.put("\\{dao.search.replace\\}", searchReplace.toString()); // dao层的搜索方法对象类替换{dao.search.replace}

			labMap.put("\\{getter\\}", getter.toString()); // 对象getter/setter方法：{getter}
			labMap.put("\\{mapper\\}", mapper.toString()); // mybatis中的mapper.xml

			labMap.put("\\{mapperBatch\\}",
					mapperBatch.delete(mapperBatch.length() - 2, mapperBatch.length()).toString()); // mybatis中的mapper.xml
			labMap.put("\\{mapperBatchValues\\}",
					mapperBatchValues.delete(mapperBatchValues.length() - 2, mapperBatchValues.length()).toString());
			labMap.put("\\{vue.temp\\}", vueTemp.delete(vueTemp.length() - 1, vueTemp.length()).toString());

			// 排序
			if (StringUtils.isNotEmpty(orderField)) {
				labMap.put("\\{order.field\\}", orderField);
				labMap.put("\\{order.by\\}",
						"ORDER BY " + model + "." + orderField + " DESC, " + model + ".UPDATE_TIME DESC");
			} else {
				labMap.put("\\{order.by\\}", "ORDER BY " + model + ".UPDATE_TIME DESC");
			}

			labMap.put("\\{service.param\\}", serviceParam.toString()); // 业务层处理页面返回对象方法：{service.param}

		} finally {
			db.CloseConnection();
		}
	}

	/**
	 * 删除表名前缀
	 *
	 * @author lh_neu
	 * @date 2013-9-5 下午04:17:49
	 * @param tableName
	 * @return
	 */
	private String delBeforeName(final String tableName) {
		String objName = "";
		// 有前缀的话，将前缀去掉在生成对象名称
		if (StringUtil.isNotEmpty(dbInfo.getBeforeName())) {
			for (final String beforeName : dbInfo.getBeforeName()) {
				if (tableName.toLowerCase().indexOf(beforeName.toLowerCase()) >= 0) {
					objName = StringUtils.substringAfter(tableName.toLowerCase(), beforeName.toLowerCase());
				}
			}

		} else {
			objName = tableName;
		}
		return objName;
	}

	private String getFieldGetterName(final String fieldName, final String fieldType) {
		if (StringUtils.isEmpty(fieldName)) {
			return "";
		}
		return "get" + StringUtils.capitalize(fieldName);
	}

	private String getFieldSetterName(final String fieldName, final String fieldType) {
		if (StringUtils.isEmpty(fieldName)) {
			return "";
		}
		return "set" + StringUtils.capitalize(fieldName);
	}

	/**
	 * 获取数据库返回值的类型
	 *
	 * @author lh_neu
	 * @date 2013-9-5 下午05:32:06
	 * @param dataType
	 * @return
	 */
	private String getResultSetType(final String dataType) {
		if (StringUtils.isEmpty(dataType)) {
			return "";
		}
		boolean isInt = false;
		boolean isFloat = false;
		boolean isDouble = false;
		boolean isDate = false;
		if (dataType.trim().equalsIgnoreCase("int") || dataType.trim().equalsIgnoreCase("long")
				|| dataType.trim().equalsIgnoreCase("integer")) {
			isInt = true;
		} else if (dataType.trim().equalsIgnoreCase("datetime") || dataType.trim().equalsIgnoreCase("date")
				|| dataType.trim().equalsIgnoreCase("time")) {
			isDate = true;
		} else if (dataType.trim().equalsIgnoreCase("float") || dataType.trim().equalsIgnoreCase("real")) {
			isFloat = true;
		} else if (dataType.trim().equalsIgnoreCase("double") || dataType.trim().equalsIgnoreCase("decimal")
				|| dataType.trim().equalsIgnoreCase("number") || dataType.trim().equalsIgnoreCase("numeric")
				|| dataType.trim().equalsIgnoreCase("number")) {
			isDouble = true;
		}

		if (isFloat) {
			return "Float";
		}
		if (isDouble) {
			return "Double";
		}
		if (isDate) {
			return "Timestamp";
		}
		if (isInt) {
			return "Int";
		}

		return "String";
	}

	private void replaceFileName(final String objPath, final String objName, final Map<String, String> labMap) {
		if (StringUtils.isEmpty(objPath)) {
			return;
		}
		try {
			// 返回一个map，有两个健（folders：文件夹名称、files：文件名称、times：文件修改时间）
			final Map<String, List<String>> map = FileUtil.readfile(objPath);
			if ((map != null) && !map.isEmpty()) {
				final List<String> files = map.get("files");
				final List<String> fileNames = map.get("fileNames");
				for (int i = 0; i < files.size(); i++) {

					// 替换目录下的index.jsp文件中的标签

					FileUtil.replaceFileContent(objPath + File.separator + files.get(i), EngineerUtil.FILE_UNCODE,
							labMap);

					FileUtil.renameFolder(objPath + File.separator + files.get(i),
							fileNames.get(i).replaceAll("\\{obj\\}", objName).replaceAll("\\{obj.lower\\}",
									StringUtils.lowerCase(objName)));
				}
			}
		} catch (final IOException e) {
			e.printStackTrace();
		}

	}

	private String getClassName(String tableName) {
		if (StringUtils.isEmpty(tableName)) {
			return "";
		}
		// 替换“_2_”为"_to_"
		tableName = tableName.replaceAll("_2_", "_to_");
		tableName = tableName.replaceAll("2", "_to_");
		final String objName = delBeforeName(tableName);
		// 首字母大写
		return StringUtils.capitalize(makeName(objName));
	}

	private String getFieldName(final String tableName, final String className, String columnName) {
		if (StringUtils.isEmpty(columnName)) {
			return "";
		}
		// 若列的“_”开头一段与表名去掉前缀的一致，则将第一段去掉。例如：表名s_admin，列名admin_name，则列名直接去name
		if (StringUtils.indexOf(StringUtils.upperCase(columnName), StringUtils.upperCase(className)) == 0) {
			columnName = StringUtils.substringAfter(StringUtils.upperCase(columnName),
					StringUtils.upperCase(className));
		}

		return makeName(columnName);
	}

	/**
	 * 生成名称，首字母小写，后词组首字母大写
	 *
	 * @author lh_neu
	 * @date 2013-9-5 下午04:19:19
	 * @param objName
	 * @return
	 */
	private String makeName(String objName) {
		// 全部转换为小写
		objName = StringUtils.trimToEmpty(StringUtils.lowerCase(objName));

		final String[] split = StringUtil.str2Arr(objName, "_");
		final StringBuffer objNameStr = new StringBuffer("");
		if ((split != null) && (split.length >= 1)) {
			for (final String string : split) {
				// 首字母大写
				objNameStr.append(StringUtils.capitalize(string));
			}
		}
		objName = StringUtils.uncapitalize(objNameStr.toString());
		if ("order".equals(objName)) {
			objName = "myOrder";
		}
		return objName;
	}

	private String getTableComment(String tableComment) {
		if (StringUtils.isEmpty(tableComment)) {
			return "";
		}
		if (tableComment.indexOf("; InnoDB free") >= 0) {
			tableComment = StringUtils.substringBefore(tableComment, "; InnoDB free");
		}
		return tableComment;

	}
}
